#include "unit.h"


//selftest+++++++++++++++++++++++++++++++++++++++
// tests the unit classes
//-----------------------------------------------
int Units::selftest(){
    srand(50);
    cout << "--bullet equality testing--" << endl;
    bullet* b = new bullet();
    b->damage(5);
    b->accuracy(5);
    bullet* c = new bullet();
    c->damage(5);
    c->accuracy(5);
    cout << "--" << (b == c) << endl;
    cout << "--" << (*b == *c) << endl;
    cout << "--subunit combat testing--" << endl;
    subunit su1 = subunit(3,0,40,5);
    subunit su2 = subunit(3,1,30,4);
    subunit su3 = subunit(3,3,20,3);
    cout << &su1 << " " << &su2 << " " << &su3 << endl;
    su1.addBatery(foreward, 3, 80);
    su2.addBatery(foreward, 3, 80);
    su3.addBatery(foreward, 3, 80);
    voley vol = voley(0,0);
    vol.pushBullet(su1.shoot(foreward), 20);
    cout << "vol size" << vol.size() << endl;
    cout << "dead --" << su1.dead(20, &vol, true) << "--" << endl;
    cout << "vol size" << vol.size() << endl;
    vol.pushBullet(su1.shoot(foreward), 20);
    cout << "vol size" << vol.size() << endl;
    cout << "dead --" << su2.dead(20, &vol, true) << "--" << endl;
    cout << "vol size" << vol.size() << endl;
    vol.pushBullet(su1.shoot(foreward), 20);
    cout << "vol size" << vol.size() << endl;
    cout << "dead --" << su3.dead(20, &vol, true) << "--" << endl;
    cout << "vol size" << vol.size() << endl;
    cout << "--fleet tests--" << endl;
    unit u1 = unit();
    unit u2 = unit();
    u1.add(&su1, 12000);
    u2.add(&su2, 4000);
    u2.add(&su1, 4000);
    u2.add(&su3, 4000);
    u1.resetAP();
    u2.resetAP();
    cout << u1.move(20, 20) << "--tf" << endl;
    cout << u2.move(20, 10) << "--tf" << endl;
    cout << "--compositions--" << endl;
    cout << "--u1--" << endl;
    cout << u1.nums(&su1) << endl;
    cout << u1.nums(&su2) << endl;
    cout << u1.nums(&su3) << endl;
    cout << "--u2--" << endl;
    cout << u2.nums(&su1) << endl;
    cout << u2.nums(&su2) << endl;
    cout << u2.nums(&su3) << endl;
    cout << "--ap test--" << endl;
    u1.resetAP();
    u2.resetAP();
    cout << "u1: " << u1.apoints() << endl;
    cout << "u2: " << u2.apoints() << endl;
    cout << "--directoinality--" << endl;
    cout << u2.relation(u1.posX(), u1.posY()) <<endl;
    cout << "moving... (20, 20) * of (20, 30) " << u2.move(20,30) << endl;
    cout << "confirm: " <<  u2.posX() << ", " << u2.posY() << endl;
    u2.resetAP();
    cout << u2.relation(u1.posX(), u1.posY()) <<endl;
    cout << "moving... (20, 20) * of (10, 20) " << u2.move(10,20) << endl;
    cout << "confirm: " <<  u2.posX() << ", " << u2.posY() << endl;
    u2.resetAP();
    cout << u2.relation(u1.posX(), u1.posY()) <<endl;
    cout << "moving... (20, 20) * of (30, 20) " << u2.move(30,20) << endl;
    cout << "confirm: " << u2.posX() << ", " << u2.posY() << endl;
    u2.resetAP();
    cout << u2.relation(u1.posX(), u1.posY()) <<endl;
    cout << "moving... (20, 20) * of (10, 10) " << u2.move(10,10) << endl;
    cout << "confirm: " <<  u2.posX() << ", " << u2.posY() << endl;
    u2.resetAP();
    cout << u2.relation(u1.posX(), u1.posY()) <<endl;
    cout << "moving... (20, 20) * of (30, 30) " << u2.move(30,30) << endl;
    cout << "confirm: " <<  u2.posX() << ", " << u2.posY() << endl;
    u2.resetAP();
    cout << u2.relation(u1.posX(), u1.posY()) <<endl;
    cout << "--u2 is facing down--" << endl;
    u2.face(down);
    cout << "--unit combat test--" << endl;
    u1.power(1);
    u1.speed(40);
    u1.cohesion(50);
    u2.power(2);
    u2.speed(40);
    u2.cohesion(50);
    voley* v = u1.shoot(0,0,Units::foreward);
    if(v == NULL){
        cout << "NULL VOLEY" << endl;
    }else {
        cout << "vol size" << v->size() << endl;
        v->print();
    }
    u2.takeDamage(v);
    cout << "--u2--" << endl;
    cout << u2.nums(&su1) << endl;
    cout << u2.nums(&su2) << endl;
    cout << u2.nums(&su3) << endl;

    return 0;
}
//===============================================
//bullet+++++++++++++++++++++++++++++++++++++++++
// a simple struct to hold weapon information. it
// will be both the bullet and the weapon
//-----------------------------------------------
Units::bullet::bullet(){
    dam = 0;
    acc = 0;
    mxrange = 0;
    mnrange = 0;
    apcost = 1;
}
Units::bullet::bullet(bullet* copyme){
    dam = copyme->dam;
    acc = copyme->acc;
    mxrange = copyme->mxrange;
    mnrange = copyme->mnrange;
    apcost = copyme->apcost;
}
int Units::bullet::damage(int val){
    if(val >= 0){
        dam = val;
    } else {
        dam = 0;
    }
    return dam;
}
int Units::bullet::damage(){
    return dam;
}
int Units::bullet::accuracy(int val){
    if(val > 0){
        acc = val;
    } else {
        acc = 0;
    }
    return acc;   
}
int Units::bullet::accuracy(){
    return acc;
}
int Units::bullet::minRange(int val){
    if(val >= 0){
        mnrange = val;
    } else {
        mnrange = 0;
    }
    if(mxrange < mnrange){
        mxrange = mnrange;
    }
    return mnrange;
}
int Units::bullet::minRange(){
    return mnrange;
}
int Units::bullet::maxRange(int val){
    if(val >= mnrange){
        mxrange = val;
    } else {
        mxrange = mnrange;
    }
    return mxrange;
}
int Units::bullet::maxRange(){
    return mxrange;
}
int Units::bullet::apCost(int val){
    if(val > 0){
        apcost = val;
    }
    return apcost;
}
int Units::bullet::apCost(){
    return apcost;
}
Units::bullet* Units::bullet::shoot(){
    return new bullet(this);
}
bool Units::bullet::operator ==(bullet& b){
    return ((dam == b.dam) && (acc = b.acc));
}
//===============================================
//voley++++++++++++++++++++++++++++++++++++++++++
// a simple struct to hold a list of bullets.  maps
// the pointer to the number of times it's present
//-----------------------------------------------
Units::voley::voley(int x, int y){
    sourcex = x;
    sourcey = y;
    rounds.clear();
}
Units::bullet* Units::voley::nextShot(){
    int rng = 0;
    while(!types.empty()){
        rng = rand() % types.size();
        if(rounds[types[rng]] > 0){
            rounds[types[rng]]--;
            return types[rng];
        }
        delete types[rng];
        rounds.erase(types[rng]);
        types.erase(types.begin() + rng);
    }
    return NULL;
}
void Units::voley::pushBullet(bullet* add){
    if(add == NULL){
        return;
    }
    if(rounds.find(add) == rounds.end()){
        types.push_back(add);
        rounds[add] = 1;
    }
    rounds[add] += 1;
}
void Units::voley::pushBullet(bullet* add, int num){
    if(add == NULL || num == 0){
        return;
    }
    if(rounds.find(add) == rounds.end()){
        types.push_back(add);
        rounds[add] = num;
    } else {
        rounds[add] += num;
    }
}
void Units::voley::buff(int dam, int acc){
    cout << "buffing to: " << dam <<"; " << acc << endl;
    if(dam == 0 && acc == 0){
        return;
    }
    for(int i = 0; i < types.size(); i++){
        types[i]->damage(types[i]->damage() + dam);
        types[i]->accuracy(types[i]->accuracy() + acc);
    }
}
void Units::voley::source(int *x, int *y){
    *x = sourcex;
    *y = sourcey;
}
int Units::voley::size(){
   int sz = 0;
   for(int i = 0; i < types.size(); i++){
       sz += rounds[types[i]];
   }
   return sz;
}
void Units::voley::print(){
    map<bullet*, int>::iterator iter;
    for(iter = rounds.begin(); iter != rounds.end(); iter++){
        cout << iter->first->damage() << "; " << iter->first->accuracy() << "---" << iter->second << endl;
    }
}
//===============================================
//subunit++++++++++++++++++++++++++++++++++++++++
// the base unit. the stats for a single ship, not
// an actual instance of the ship.  those will be
// represented by an integer in unit which references
// subunit
//-----------------------------------------------
Units::subunit::subunit(int arm, int shd, int eva, int spd){
    armor = arm;
    shield = shd;
    evasion = eva;
    speed = spd;
    for(int i = 0; i < numBats; i++){
        batery[i] = NULL;
    }
}
Units::subunit::~subunit(){
    for(int i = 0; i < numBats; i++){
        if(batery[i] != NULL){
            delete batery[i];
        }
    }
}
void Units::subunit::addBatery(bateryType btp, int dam, int acc,
        int mnr, int mxr, int ap){
    if(batery[btp] == NULL){
        batery[btp] = new bullet();
    }
    batery[btp]->damage(dam);
    batery[btp]->accuracy(acc);
    batery[btp]->minRange(mnr);
    batery[btp]->maxRange(mxr);
    batery[btp]->apCost(ap);
}
Units::bullet* Units::subunit::shoot(bateryType btp){
    bullet* bt = batery[btp]->shoot();
    return bt;
}
int Units::subunit::dead(int n, voley* vol, bool shd){
    bullet* shot;
    int kia = 0;
    for(int i = 0; i < n; i++){
        shot = vol->nextShot();
        if(shd){
            if((shot->accuracy() - evasion > rand()%100) && (sunitdeath(shot->damage(), armor, shield))){
                kia++;
            }
        } else {
            if((shot->accuracy() - evasion > rand()%100) && (sunitdeath(shot->damage(), armor, 0))){
                kia++;
            }
        }
    }
    return kia;
}
//===============================================
// unit++++++++++++++++++++++++++++++++++++++++++
// a "fleet" of subunits.  has a position, a parent
// and a number assosiated with each type
//-----------------------------------------------
Units::unit::unit(){
    facing = up;
    amdead = false;
    powerstat = powerbuff = 0;
    speedstat = speedbuff = 0;
    cohesionstat = cohesionbuff = 0;
    ap = 0;
    posx = -1;
    posy = -1;
    lib = unitLib::staticLib();
    lib->record(this);
}
Units::unit::unit(unitLib* _lib){
    facing = up;
    powerstat = powerbuff = 0;
    speedstat = speedbuff = 0;
    cohesionstat = cohesionbuff = 0;
    ap = 0;
    posx = -1;
    posy = -1;
    lib = _lib;
    lib->record(this);
}
Units::unit::~unit(){
    parent->remUnit(this);
    lib->killUnit(this);
}
void Units::unit::record(unitLib* rec){
    if(lib != NULL){
        lib->removeUnit(this, posx, posy);
        lib->killUnit(this);
    }
    lib = rec;
    lib->record(this);
}
void Units::unit::add(subunit* su, int num){
    subunitList[su] += num;
}
void Units::unit::remove(subunit* su, int num){
    if(subunitList.find(su) != subunitList.end()){
        subunitList[su] -= num;
        if(subunitList[su] < 0){
            subunitList[su] = 0;
        }
    }
}
void Units::unit::setParent(unitgroup* gp){
    if(parent != NULL){
        parent->remUnit(this);
    }
    parent = gp;
}
int Units::unit::apoints(){
    return ap;
}
bool Units::unit::useap(int _ap){
    if(ap - _ap >= 0){
        ap -= _ap;
        return true;
    }
    return false;
}
void Units::unit::resetAP(){
    if(subunitList.empty()){
        ap = 0;
    }
    //vars
    map<subunit*, int>::iterator iter;
    //reset ap
    ap = subunitList.begin()->first->spd();
    //find max
    for(iter = subunitList.begin(); iter != subunitList.end(); iter++){
        if(iter->second > 0 && iter->first->spd() < ap){
            ap = iter->first->spd();
        }
    }
}
Units::direction Units::unit::relation(int x, int y){
    y -= posy;
    x -= posx;
    if(y < 0 && x <= -1 * y && x >= y){
       return up;
    }
    if(y > 0 && x >= -1 * y && x <= y){
        return down;
    }
    if(x < 0  && y < -1 * x && y > x){
        return left;
    }
    if(x > 0  && y > -1 * x && y < x){
        return right;
    }
    return up;
}
bool Units::unit::dead(){
    if(amdead){
        return amdead;
    }
    map<subunit*, int>::iterator iter;
    for(iter = subunitList.begin(); iter != subunitList.end(); iter++){
        if(iter->second > 0){
            return false;
        }
    }
    amdead = true;
    return true;
}
int Units::unit::nums(subunit* su){
    if(subunitList.find(su) != subunitList.end())
        return subunitList[su];
    return 0;
}
int Units::unit::power(int buff){
    powerbuff += buff;
    return powerstat + powerbuff;
}
int Units::unit::speed(int buff){
    speedbuff += buff;
    return speedstat + speedbuff;
}
int Units::unit::cohesion(int buff){
    cohesionbuff += buff;
    return cohesionstat + cohesionbuff;
}
bool Units::unit::move(int mx, int my){
    int dist = sqrt((posx - mx) * (posx - mx) + (posy - my) * (posy -  my));
    if(!useap(dist)){
        return false;
    }
    return place(mx, my);
}
bool Units::unit::place(int mx, int my){
    if(lib != NULL && !lib->move(this, posx, posy, mx, my)){
        return false;
    }
    posx = mx;
    posy = my;
    return true;
}
void Units::unit::face(direction dir){
    if(useap(1)){
        facing = dir;
    }
}
Units::direction Units::unit::face(){
    return facing;
}
Units::voley* Units::unit::shoot(int mx, int my, bateryType tp){
    //varblock
    int acst = 0;
    voley* vol;
    map<subunit*, int>::iterator iter;
    //check for ap - return null if not enough
    for(iter = subunitList.begin(); iter != subunitList.end(); iter++){
        if(iter->second > 0 && iter->first->spd() > acst){
            acst = iter->first->gbattery(tp)->apCost();
        }
    }
    if(!useap(acst)){
        return NULL;
    }
    //fill the voley
    vol = new voley(posx, posy);
    for(iter = subunitList.begin(); iter != subunitList.end(); iter++){
        vol->pushBullet(iter->first->shoot(tp), iter->second);
    }
    //buff and return
    vol->buff(damBuff, accBuff);
    return vol;
}
bool Units::unit::useShield(int x, int y){
    return facing == relation(x,y);
}
void Units::unit::takeDamage(voley* vol){
    //param check
    if(vol == NULL){
        return;
    }
    //varblok
    bool ushd = useShield(vol->getX(), vol->getY());
    int num;
    int total = 0;
    int size = vol->size();
    map<subunit*, int>::iterator iter;
    //assign buffs
    vol->buff((-1 * shdBuff), (-1 * evaBuff));
    vol->print();
    //find total
    for(iter = subunitList.begin(); iter != subunitList.end(); iter++){
        total += iter->second;
    }
    //do the damage
    for(iter = subunitList.begin(); iter != subunitList.end(); iter++){
        num = size * (double(iter->second)/total);
        cout << num << endl;
        iter->second -= iter->first->dead(num, vol, ushd);
        if(iter->second <= 0){
            iter->second = 0;
        }
    }
    if(dead()){
        lib->killUnit(this);
    }

}
//===============================================
// unit-group++++++++++++++++++++++++++++++++++++
// a way to group a number of units into the same
// "team"  The units share the buffes provided by
// the flagfleet and unused officers
//-----------------------------------------------
Units::unitgroup::unitgroup(unit* flag){
    flagunit = flag;
}
Units::unitgroup::~unitgroup(){
    unit* temp;
    while(!children.empty()){
        temp = children.front();
        temp->setParent(NULL);
    }
}
int Units::unitgroup::speedBuff(int set){
    if(set >= 0){
        speed = set;
    }
    return speed;
}
int Units::unitgroup::powerBuff(int set){
    if(set >= 0){
        power = set;
    }
    return power;
}
int Units::unitgroup::cohesionBuff(int set){
    if(set >= 0){
        cohes = set;
    }
    return cohes;
}
void Units::unitgroup::addUnit(unit* u){
    children.push_back(u);
    u->power(powerBuff());
    u->speed(speedBuff());
    u->cohesion(cohesionBuff());
    u->setParent(this);
}
void Units::unitgroup::remUnit(unit* u){
    list<unit*>::iterator iter = children.begin();
    while(iter != children.end() && (*iter) != u){
        iter++;
    }
    if((*iter) == u){
        children.erase(iter);
        u->power(-1*powerBuff());
        u->speed(-1*speedBuff());
        u->cohesion(-1*cohesionBuff());
        u->setParent(NULL);
    }
}
//===============================================
// unitLlib++++++++++++++++++++++++++++++++++++++
// keeps a static library freely available and
// allows for the creation of single libraries
//-----------------------------------------------
Units::unitLib* Units::unitLib::slib;
Units::unitLib* Units::unitLib::staticLib(){
    if(slib == NULL){
        slib = new unitLib();
    }
    return slib;
}
Units::unitLib::unitLib(){
}
Units::unitLib::~unitLib(){

}
void Units::unitLib::resetAP(){
    list<unit*>::iterator iter;
    for(iter = pieces.begin(); iter != pieces.end(); iter++){
        if((*iter)->dead()){
            delete *(iter);
        } else {
            (*iter)->resetAP();
        }
    }
}
bool Units::unitLib::move(unit* u, int sx, int sy, int nx, int ny){
    if(!removeUnit(u, sx, sy)){
        return false;
    }
    if(!addUnit(u, nx, ny)){
        addUnit(u, sx, sy);
        return false;
    }
    return true;
}
void Units::unitLib::record(Units::unit* addme){
    pieces.push_back(addme);
}
void Units::unitLib::killUnit(Units::unit* killme){
    pieces.remove(killme);
    removeUnit(killme, killme->posX(), killme->posY());
}
Units::unit* Units::unitLib::getUnit(int x, int y){
    return unitmap[x][y];
}
bool Units::unitLib::addUnit(Units::unit* nu, int x, int y){
    unit* temp = unitmap[x][y];
    if(temp == NULL){
        unitmap[x][y] = nu;
        return true;
    }
    return false;
}
bool Units::unitLib::removeUnit(unit* u, int x, int y){
    if(unitmap[x][y] == u){
        unitmap[x].erase(y);
        if(unitmap[x].empty()){
            unitmap.erase(x);
        }
        return true;
    } else if(unitmap[x][y] == NULL){
        return true;
    } else {
        return false;
    }
}
//===============================================
